#include <string>
#include <gtest/gtest.h>
#include "../vas_cache.h"

using namespace vas;

struct CacheTestStruct {
    int a=0;
    std::string b;
};

TEST(CacheTest, SaveLoad) {
    const std::string key = "save_load_key";
    const std::string gp = "save_load_gp";
    const std::string ns = "save_load_ns";
    int keep_size = 2;
    auto *tmp = new CacheTestStruct;
    tmp->a = 1;
    tmp->b = "a";
    SaveValue(key, tmp, keep_size, gp, ns);
    GCacheData data = GetData(key, gp, ns);
    ASSERT_EQ(data.key, key);
    ASSERT_EQ(data.group, gp);
    ASSERT_EQ(data.name_space, ns);
    ASSERT_EQ(data.keep_size, keep_size);
    ASSERT_NE(data.ctime, 0);
    ASSERT_NE(data.mtime, 0);
    auto *p = (CacheTestStruct *) data.ptr;
    ASSERT_EQ(p, tmp);
    ASSERT_EQ(p->a, tmp->a);
    ASSERT_EQ(p->b, tmp->b);

    auto *v = (CacheTestStruct *) GetValue(key, gp, ns);
    ASSERT_EQ(v, tmp);
    ASSERT_EQ(v->a, tmp->a);
    ASSERT_EQ(v->b, tmp->b);
}

TEST(CacheTest, SaveLoadDefulatValue) {
    const std::string key = "save_load_default_key";
    int keep_size = 2;
    auto *tmp = new CacheTestStruct;
    tmp->a = 1;
    tmp->b = "a";
    SaveValue(key, tmp, keep_size);
    GCacheData data = GetData(key);
    ASSERT_EQ(data.key, key);
    ASSERT_EQ(data.group, DEFAULT_GROUP);
    ASSERT_EQ(data.name_space, DEFAULT_NAMESPACE);
    ASSERT_EQ(data.keep_size, keep_size);
    ASSERT_NE(data.ctime, 0);
    ASSERT_NE(data.mtime, 0);
    auto *p = (CacheTestStruct *) data.ptr;
    ASSERT_EQ(p, tmp);
    ASSERT_EQ(p->a, tmp->a);
    ASSERT_EQ(p->b, tmp->b);

    auto *v = (CacheTestStruct *) GetValue(key);
    ASSERT_EQ(v, tmp);
    ASSERT_EQ(v->a, tmp->a);
    ASSERT_EQ(v->b, tmp->b);
}

TEST(CacheTest, SaveLoadMany) {
    const std::string key = "save_load_many_key";
    const int keep_size = 5;

    std::vector<CacheTestStruct *> values;

    for (int i = 0; i < keep_size * 2; i++) {
        auto *tmp = new CacheTestStruct;
        tmp->a = i;
        tmp->b = "a";
        SaveValue(key, tmp);
        values.push_back(tmp);
        GCacheData data = GetData(key);
        ASSERT_EQ(data.key, key);
        ASSERT_EQ(data.keep_size, keep_size);
        ASSERT_NE(data.ctime, 0);
        ASSERT_NE(data.mtime, 0);
        auto *p = (CacheTestStruct *) data.ptr;
        ASSERT_EQ(p->a, tmp->a);
        ASSERT_EQ(p->b, tmp->b);
        auto *v = (CacheTestStruct *) GetValue(key);
        ASSERT_EQ(v->a, tmp->a);
        ASSERT_EQ(v->b, tmp->b);

        std::vector<GCacheData> data_list = GetDataList(key);
        std::vector<const void *> v_list = GetValues(key);

        if (i < 5) {
            ASSERT_EQ(data_list.size(), i + 1);
            ASSERT_EQ(v_list.size(), i + 1);
        } else {
            ASSERT_EQ(data_list.size(), keep_size);
            ASSERT_EQ(v_list.size(), keep_size);
        }
        int check_size = std::min(keep_size, i + 1);
        for (std::size_t j = 0; j < check_size; j++) {
            CacheTestStruct *local = values[values.size() - 1 - j];
            auto *v_save = (CacheTestStruct *) v_list.at(j);
            auto *d_save = (CacheTestStruct *) data_list.at(j).ptr;
            ASSERT_EQ(d_save->a, local->a);
            ASSERT_EQ(d_save->b, local->b);
            ASSERT_EQ(v_save->a, local->a);
            ASSERT_EQ(v_save->b, local->b);
        }

    }
}

TEST(CacheTest, WrongKeepSize) {
    const std::string wrong_keep_size_key = "wrong_keep";
    ASSERT_THROW(SaveValue(wrong_keep_size_key, nullptr, 0), VasInvalidValue);
    ASSERT_THROW(SaveValue(wrong_keep_size_key, nullptr, -1), VasInvalidValue);
    ASSERT_THROW(SaveValue(wrong_keep_size_key, nullptr, 10001), VasInvalidValue);
}

TEST(CacheTest, WrongDataNames) {
    const std::string wrong_key = "key@";
    const std::string wrong_group = "gr@oup";
    const std::string wrong_name_space = "@namespace";
    ASSERT_THROW(SaveValue(wrong_key, nullptr, 3), VasInvalidValue);
    ASSERT_THROW(SaveValue(wrong_key, nullptr, 3, wrong_group), VasInvalidValue);
    ASSERT_THROW(SaveValue(wrong_key, nullptr, 3, wrong_group, wrong_name_space),
                 VasInvalidValue);
}

TEST(CacheTest, GetNoExistData) {
    const std::string no_exist_key = "no_exist_key";
    const std::string no_exist_group = "no_exist_group";
    const std::string no_exist_name_space = "no_exist_name_space";
    ASSERT_THROW(GetData(no_exist_key), VasNotFound);
    ASSERT_THROW(GetData(no_exist_key, no_exist_group), VasNotFound);
    ASSERT_THROW(GetData(no_exist_key, no_exist_group, no_exist_name_space), VasNotFound);
    ASSERT_THROW(GetDataList(no_exist_key), VasNotFound);
    ASSERT_THROW(GetDataList(no_exist_key, no_exist_group), VasNotFound);
    ASSERT_THROW(GetDataList(no_exist_key, no_exist_group, no_exist_name_space), VasNotFound);
    ASSERT_THROW(GetValue(no_exist_key), VasNotFound);
    ASSERT_THROW(GetValue(no_exist_key, no_exist_group), VasNotFound);
    ASSERT_THROW(GetValue(no_exist_key, no_exist_group, no_exist_name_space), VasNotFound);
    ASSERT_THROW(GetValues(no_exist_key), VasNotFound);
    ASSERT_THROW(GetValues(no_exist_key, no_exist_group), VasNotFound);
    ASSERT_THROW(GetValues(no_exist_key, no_exist_group, no_exist_name_space), VasNotFound);
}